﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XRpgLibrary.TileEngine
{
    public class TileMap
    {
        #region Field Region
        
        List<Tileset> tilesets;
        List<MapLayer> mapLayers;
        List<TileAnimation> tileAnimations;
        
        int mapWidth = -1;
        int mapHeight = -1;
        static int currentMapWidth = -1;
        static int currentMapHeight = -1;

        #endregion
        
        #region Property Region

        private List<TileAnimation> TileAnimations
        {
            get
            {
                if (tileAnimations == null)
                    tileAnimations = new List<TileAnimation>();

                return tileAnimations;
            }
        }

        public int MapWidth
        {
            get
            {
                if (mapWidth < 0 && MapLayers.Count > 0)
                {
                    mapWidth = MapLayers[0].Width;
                    TileMap.currentMapWidth = mapWidth;
                }

                return mapWidth;
            }
            private set
            {
                mapWidth = value;
                TileMap.currentMapWidth = value;
            }
        }

        public int MapHeight
        {
            get
            {
                if (mapHeight < 0 && MapLayers.Count > 0)
                {
                    mapHeight = MapLayers[0].Height;
                    TileMap.currentMapHeight = mapHeight;
                }

                return mapHeight;
            }
            private set
            {
                mapHeight = value;
                TileMap.currentMapHeight = value;
            }
        }

        private List<Tileset> Tilesets
        {
            get 
            {
                if (tilesets == null)
                    tilesets = new List<Tileset>();

                return tilesets; 
            }
            set { tilesets = value; }
        }

        public List<MapLayer> MapLayers
        {
            get 
            {
                if (mapLayers == null)
                    mapLayers = new List<MapLayer>();

                return mapLayers; 
            }
            set { mapLayers = value; }
        }

        public static int WidthInPixels
        {
            get 
            {
                return currentMapWidth * Engine.TileWidth; 
            }
        }

        public static int HeightInPixels
        {
            get 
            {
                return currentMapHeight * Engine.TileHeight; 
            }
        }
        #endregion
        
        #region Constructor Region

        public TileMap(List<Tileset> tilesets, List<MapLayer> layers)
        {
            Initialize(tilesets, layers);
        }

        public TileMap(Tileset tileset, MapLayer baseLayer)
        {
            List<Tileset> t = new List<Tileset>();
            t.Add(tileset);

            List<MapLayer> l = new List<MapLayer>();
            l.Add(baseLayer);

            Initialize(t, l);
        }
        #endregion

        private void Initialize(List<Tileset> tilesets, List<MapLayer> layers)
        {
            Tilesets = tilesets;
            AddLayers(layers);

            InitializeTileAnimations();
        }

        private void InitializeTileAnimations()
        {
            for(int tIndex = 0, cnt = Tilesets.Count; tIndex < cnt; tIndex++)
            {
                if (string.IsNullOrEmpty(Tilesets[tIndex].AnimationScript))
                    continue;

                string[] scripts = Tilesets[tIndex].AnimationScript.Trim().Split('\n');
                
                foreach (string script in scripts)
                {
                    string cleanScript = script.Trim();
                    if (string.IsNullOrEmpty(cleanScript) || cleanScript.StartsWith("#"))
                        continue;

                    TileAnimation tileAnimation = TileAnimation.GenerateTileAnimation(cleanScript);

                    if (tileAnimation.Count < 2)
                        throw new Exception("Malformed Tile Animation Script - specify at least 2 tiles to animate.");

                    if (PlaceAnimatedTile(tIndex, tileAnimation))
                        TileAnimations.Add(tileAnimation);
                    
                }
            }
        }

        private bool PlaceAnimatedTile(int tilesetIndex, TileAnimation tileAnimation)
        {
            bool placed = false;
            foreach (MapLayer layer in MapLayers)
            {
                if (layer.TilesetIndex != tilesetIndex)
                    continue;

                for(int y = 0, ycnt = layer.Height; y < ycnt; y++)
                    for (int x = 0, xcnt = layer.Width; x < xcnt; x++)
                    {
                        Tile tile = layer.GetTile(x, y);
                        if (tile.TileIndex == tileAnimation.FirstTileIndex)
                        {
                            layer.SetTile(x, y, new AnimatedTile(tileAnimation));
                            placed = true;
                        }
                    }
            }

            return placed;
        }

        public void AddTileset(Tileset tileset)
        {
            tilesets.Add(tileset);
        }

        public void AddLayers(List<MapLayer> layers)
        {
            foreach (MapLayer layer in layers)
                AddLayer(layer);
        }
        

        public void AddLayer(MapLayer layer)
        {
            if (MapWidth > 0 && MapHeight > 0 && layer.Width != MapWidth && layer.Height != MapHeight)
                throw new Exception("Map layer  is not the same size as the map");

            MapLayers.Add(layer);
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Camera camera)
        {
            for(int i = mapLayers.Count - 1; i > -1; i--) 
                mapLayers[i].Draw(gameTime, spriteBatch, camera, tilesets);
        }

        internal void Update(GameTime gameTime)
        {
            foreach (MapLayer layer in mapLayers)
                layer.Update(gameTime);

            foreach (TileAnimation ta in TileAnimations)
                ta.Update(gameTime);
        }

        public void InsertPlayer(int layerIndex, CharacterClasses.Character character)
        {
            mapLayers[layerIndex].CharacterManager.AddPlayer(character);

        }
    }
}